﻿
using UnityEngine;
using System;
using System.Collections.Generic;
//using System.ComponentModel;
//using System.Data;
//using System.Drawing;
using System.Linq;
//using System.Threading.Tasks;
//using System.Windows.Forms;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using System.IO;
using System.Text;

[Serializable]
public class SocketServer{

    ////定义接收客户端发送消息的回调
    public delegate void ReceiveMsgCallBack(TransMsg strReceive);
    ////声明
    public ReceiveMsgCallBack ReceiveCallBack;

    //用于通信的Socket
    Socket socketSend;
    //用于监听的SOCKET
    Socket socketWatch;

    //将远程连接的客户端的IP地址和Socket存入集合中
    Dictionary<string, Socket> dicSocket = new Dictionary<string, Socket>();

    //创建监听连接的线程
    Thread AcceptSocketThread;
    bool _socketColseFlag;
    //接收客户端发送消息的线程
    Thread threadReceive;
    bool _receiveColseFlag;

    Thread _clientCheckThread;//客户端心跳检测线程
    bool _clientCheckThreadColseFlag;

    private Action<string> _dele_OnGetIp;

    public Action<string> Dele_OnGetIp
    {
        get
        {
            return _dele_OnGetIp!=null?_dele_OnGetIp:_dele_OnGetIp+=(x)=> { };
        }

        set
        {
            _dele_OnGetIp = value;
        }
    }

    /// <summary>
    /// 开始监听
    /// </summary>
    public void StartListen(string ip, string endpoint)
    {
        //try
        {
            //当点击开始监听的时候 在服务器端创建一个负责监听IP地址和端口号的Socket
            socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            ////获取ip地址
            //IPAddress ip = IPAddress.Parse(GetIpAddress());//ip地址创建

            IPEndPoint point = new IPEndPoint(IPAddress.Parse(ip), int.Parse(endpoint));//指定IP端口号链接(监听自己的ip、端口)
                                                                                        //绑定IP地址和端口号
            socketWatch.Bind(point);
            //this.txt_Log.AppendText("监听成功" + " \r \n");
            //开始监听:设置最大可以同时连接多少个请求
            socketWatch.Listen(10);

            //实例化回调
            //setCallBack = new SetTextValueCallBack(SetTextValue);
            //receiveCallBack = new ReceiveMsgCallBack(ReceiveMsg);
            //setCmbCallBack = new SetCmbCallBack(AddCmbItem);
            //sendCallBack = new SendFileCallBack(SendFile);

            //创建线程
            Debug.Log("-------------创建监听线程");
            AcceptSocketThread = new Thread(new ParameterizedThreadStart(StartListen));
            AcceptSocketThread.IsBackground = true;
            AcceptSocketThread.Start(socketWatch);

            _clientCheckThread = new Thread(ClientCheck);///检测更新移除断开连接的客户端
            _clientCheckThread.IsBackground = true;
            _clientCheckThread.Start();
        }
        //catch (Exception ex)
        //{
        //    Debug.LogWarning(ex.ToString());
        //}

    }

    /// <summary>
    /// 等待客户端的连接，并且创建与之通信用的Socket
    /// </summary>
    /// <param name="obj"></param>
    private void StartListen(object obj)
    {
        Socket socketWatch = obj as Socket;
        while (true)
        {
            //等待客户端的连接，并且创建一个用于通信的Socket
            socketSend = socketWatch.Accept();
            //获取远程主机的ip地址和端口号
            //string strIp = socketSend.RemoteEndPoint.ToString();//本来记录的key值是ip加分配的客户端端口号，但是由于此时控制端只知道IP，不知道端口号，这里暂时把key值改为只要ip，缺点是一个ip只持有一个连接
            string strIp = socketSend.RemoteEndPoint.ToString().Split(':')[0];
            dicSocket.Add(strIp, socketSend);
            //this.cmb_Socket.Invoke(setCmbCallBack, strIp);
            string strMsg = "远程主机：" + socketSend.RemoteEndPoint + "连接成功";
            //使用回调
            //txt_Log.Invoke(setCallBack, strMsg);

            //定义接收客户端消息的线程
            Thread threadReceive = new Thread(new ParameterizedThreadStart(Receive));
            threadReceive.IsBackground = true;
            threadReceive.Start(socketSend);
                       
        }
    }

    /// <summary>
    /// 服务器端不停的接收客户端发送的消息
    /// </summary>
    /// <param name="obj"></param>
    private void Receive(object obj)
    {
        try
        {
            Socket socketSend = obj as Socket;
            while (true)
            {
                //客户端连接成功后，服务器接收客户端发送的消息
                byte[] buffer = new byte[2048];
                //实际接收到的有效字节数
                int count = socketSend.Receive(buffer);
                if (count == 0)//count 表示客户端关闭，要退出循环
                {
                    break;
                }
                else
                {
                    buffer = buffer.Take(count).ToArray();
                    TransMsg msg = SocketTool.TransByte2Msg(buffer);
                    string strReceiveMsg = "接收：" + socketSend.RemoteEndPoint + "发送的消息:" + msg._msgType;
                    Debug.Log(strReceiveMsg);
                    ReceiveCallBack?.Invoke(msg);
                    
                }
            }
        }
        catch(Exception e)
        {
            ReceiveCallBack?.Invoke(new TransMsg("error", e.ToString()));
        }

    }

    /// <summary>
    /// 服务器给客户端发送消息
    /// </summary>
    public void Send(string msgType,string strMsg,string sendIp=null)
    {
        try
        {
            
            //将泛型集合转换为数组
            byte[] newBuffer = SocketTool.TransMsg2Byte(msgType,strMsg);
            if(sendIp!=null)
            {
                Socket sk;
                if (dicSocket.TryGetValue(sendIp, out sk))
                {
                    sk.Send(newBuffer);
                    Debug.Log(string.Format("服务器给客户端({0})发送消息:{1}", sendIp, strMsg));
                    //SocketDebug._socketDebug.Enqueue(string.Format("服务器给客户端({0})发送消息:{1}", sendIp, strMsg));
                }
                else
                {
                    Debug.Log("准备发送，Send，未找到：" + sendIp);
                }
            }
            else
            {
                foreach(var i in dicSocket)
                {
                   i.Value.Send(newBuffer);
                }
                Debug.Log(string.Format("服务器给客户端({0})发送消息:{1}","All", strMsg));
                //ReceiveCallBack?.Invoke(new TransMsg("error", ex.ToString()));
            }
            //获得用户选择的IP地址
            //string ip = this.cmb_Socket.SelectedItem.ToString();
            //dicSocket[ip].Send(newBuffer);
            //Debug.Log("服务器给客户端发送消息:\n" + strMsg);
        }
        catch (Exception ex)
        {
            Debug.LogWarning( ex.ToString());
        }
        //socketSend.Send(buffer);
    }

    public void ClientCheck()//客户端检测（心跳检测，如果检测到已经断开连接的scoket，将其scoket移除）
    {
        try
        {
            while(true)
            {
                List<KeyValuePair<string, Socket>> dirMemberList = dicSocket.ToList();
                foreach (var i in dirMemberList)
                {
                    //SocketDebug._socketDebug.Enqueue(i.Key + "=" + dicSocket.Count);
                    ReceiveCallBack?.Invoke(new TransMsg("debug", i.Key + "=" + dicSocket.Count));
                    if (i.Value.Poll(1000, SelectMode.SelectRead))
                    {
                        i.Value.Close();
                        dicSocket.Remove(i.Key);
                        //SocketDebug._socketDebug.Enqueue(i.Key + "  is leave,remove.Current socket num=" + dicSocket.Count);
                        ReceiveCallBack?.Invoke(new TransMsg("debug", i.Key + "  is leave,remove.Current socket num=" + dicSocket.Count));
                    }
                }
                //threadError ="socket num" + dirMemberList.Count;
                Thread.Sleep(1000);
            }
        }
        catch (Exception e)
        {
            ReceiveCallBack?.Invoke(new TransMsg("debug", e.ToString()));
        }
     
       
    }

    /// <summary>
    /// 停止监听
    /// </summary>
    public void StopListen_Click()
    {
        if(socketWatch!=null)
            socketWatch.Close();
        if(socketSend!=null)
            socketSend.Close();
        //终止线程
        if(AcceptSocketThread!=null)
        {
            AcceptSocketThread.Abort();
            AcceptSocketThread.Join();
        }
            
        if(threadReceive!=null)
        {
            threadReceive.Abort();
            threadReceive.Join();
        }
          if(_clientCheckThread!=null)
        {
            _clientCheckThread.Abort();
            _clientCheckThread.Join();
        }
    }

}
